Preamble

# Clear workspace
rm(list=ls()); graphics.off() 
### Load packages
library(tidyverse) # Collection of all the good stuff like dplyr, ggplot2 ect.
library(magrittr) # For extra-piping operators (eg. %<>%)
library(skimr) # For nice data summaries

The InsideAirBnB data

Instroduction

  • The data is sourced from the Inside Airbnb which hosts publicly available data from the Airbnb site.
  • Interactive visualizations are provided here

The dataset comprises of three main tables:

  • listings - Detailed listings data showing 96 atttributes for each of the listings. Some of the attributes which are intuitivly interesting are: price (continuous), longitude (continuous), latitude (continuous), listing_type (categorical), is_superhost (categorical), neighbourhood (categorical), ratings (continuous) among others.
  • reviews - Detailed reviews given by the guests with 6 attributes. Key attributes include date (datetime), listing_id (discrete), reviewer_id (discrete) and comment (textual).
  • calendar - Provides details about booking for the next year by listing. Four attributes in total including listing_id (discrete), date (datetime), available (categorical) and price (continuous).

Load data

listings <- read_csv('http://data.insideairbnb.com/denmark/hovedstaden/copenhagen/2020-06-26/data/listings.csv.gz')
listings %>% glimpse()
Rows: 28,523
Columns: 106
$ id                                           <dbl> 6983, 26057, 26473, 29118, 29618, 310…
$ listing_url                                  <chr> "https://www.airbnb.com/rooms/6983", …
$ scrape_id                                    <dbl> 20200626200423, 20200626200423, 20200…
$ last_scraped                                 <date> 2020-06-28, 2020-06-28, 2020-06-28, …
$ name                                         <chr> "Copenhagen 'N Livin'", "Lovely house…
$ summary                                      <chr> "Lovely apartment located in the hip …
$ space                                        <chr> "Beautiful and cosy apartment conveni…
$ description                                  <chr> "Lovely apartment located in the hip …
$ experiences_offered                          <chr> "none", "none", "none", "none", "none…
$ neighborhood_overview                        <chr> "Nice bars and cozy cafes just minute…
$ notes                                        <chr> NA, NA, NA, NA, "Please note that the…
$ transit                                      <chr> "Bus 66 runs to the central station. …
$ access                                       <chr> "Bedroom, living room, kitchen, and b…
$ interaction                                  <chr> "We are usually at work during day ti…
$ house_rules                                  <chr> "No smoking allowed! No pets.", "We w…
$ thumbnail_url                                <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ medium_url                                   <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ picture_url                                  <chr> "https://a0.muscache.com/im/pictures/…
$ xl_picture_url                               <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ host_id                                      <dbl> 16774, 109777, 112210, 125230, 127577…
$ host_url                                     <chr> "https://www.airbnb.com/users/show/16…
$ host_name                                    <chr> "Simon", "Kari", "Oliver", "Nana", "S…
$ host_since                                   <date> 2009-05-12, 2010-04-17, 2010-04-22, …
$ host_location                                <chr> "Copenhagen, Capital Region of Denmar…
$ host_about                                   <chr> "I'm currently working as an environm…
$ host_response_time                           <chr> "N/A", "N/A", "within a few hours", "…
$ host_response_rate                           <chr> "N/A", "N/A", "100%", "N/A", "N/A", "…
$ host_acceptance_rate                         <chr> "33%", "19%", "100%", "17%", "N/A", "…
$ host_is_superhost                            <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FA…
$ host_thumbnail_url                           <chr> "https://a0.muscache.com/im/users/167…
$ host_picture_url                             <chr> "https://a0.muscache.com/im/users/167…
$ host_neighbourhood                           <chr> "Nørrebro", "Indre By", "Indre By", "…
$ host_listings_count                          <dbl> 1, 1, 4, 1, 1, 1, 3, 1, 0, 2, 1, 1, 2…
$ host_total_listings_count                    <dbl> 1, 1, 4, 1, 1, 1, 3, 1, 0, 2, 1, 1, 2…
$ host_verifications                           <chr> "['email', 'phone', 'reviews']", "['e…
$ host_has_profile_pic                         <lgl> TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, T…
$ host_identity_verified                       <lgl> FALSE, FALSE, TRUE, FALSE, TRUE, FALS…
$ street                                       <chr> "Copenhagen, Hovedstaden, Denmark", "…
$ neighbourhood                                <chr> "Nørrebro", "Indre By", "Indre By", "…
$ neighbourhood_cleansed                       <chr> "Nrrebro", "Indre By", "Indre By", "V…
$ neighbourhood_group_cleansed                 <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ city                                         <chr> "Copenhagen", "Copenhagen", "Copenhag…
$ state                                        <chr> "Hovedstaden", "Hovedstaden", "Hoveds…
$ zipcode                                      <chr> "2200", "2100", "1210", "1650", "2100…
$ market                                       <chr> "Copenhagen", "Copenhagen", "Copenhag…
$ smart_location                               <chr> "Copenhagen, Denmark", "Copenhagen, D…
$ country_code                                 <chr> "DK", "DK", "DK", "DK", "DK", "DK", "…
$ country                                      <chr> "Denmark", "Denmark", "Denmark", "Den…
$ latitude                                     <dbl> 55.68798, 55.69163, 55.67590, 55.6706…
$ longitude                                    <dbl> 12.54571, 12.57459, 12.57698, 12.5543…
$ is_location_exact                            <lgl> TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, T…
$ property_type                                <chr> "Apartment", "House", "House", "Apart…
$ room_type                                    <chr> "Private room", "Entire home/apt", "E…
$ accommodates                                 <dbl> 2, 6, 12, 2, 4, 3, 3, 4, 5, 2, 2, 2, …
$ bathrooms                                    <dbl> 1.0, 1.5, 2.5, 1.0, 1.0, 1.0, 2.0, 1.…
$ bedrooms                                     <dbl> 1, 4, 6, 1, 3, 1, 1, 2, 2, 1, 1, 1, 1…
$ beds                                         <dbl> 1, 4, 7, 1, 3, 3, 2, 2, 1, 1, 0, 1, 1…
$ bed_type                                     <chr> "Real Bed", "Real Bed", "Real Bed", "…
$ amenities                                    <chr> "{TV,\"Cable TV\",Wifi,Kitchen,\"Paid…
$ square_feet                                  <dbl> 97, NA, NA, NA, NA, 689, NA, 807, NA,…
$ price                                        <chr> "$365.00", "$2,398.00", "$3,096.00", …
$ weekly_price                                 <chr> NA, NA, "$17,513.00", NA, "$2,981.00"…
$ monthly_price                                <chr> NA, NA, "$67,073.00", NA, "$8,943.00"…
$ security_deposit                             <chr> "$0.00", "$5,000.00", "$3,726.00", NA…
$ cleaning_fee                                 <chr> "$33.00", "$1,100.00", "$522.00", "$3…
$ guests_included                              <dbl> 1, 3, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 2…
$ extra_people                                 <chr> "$66.00", "$350.00", "$0.00", "$0.00"…
$ minimum_nights                               <dbl> 2, 3, 3, 7, 7, 2, 3, 6, 5, 30, 1, 3, …
$ maximum_nights                               <dbl> 15, 30, 31, 14, 31, 10, 365, 1125, 21…
$ minimum_minimum_nights                       <dbl> 2, 3, 3, 3, 7, 2, 3, 6, 5, 30, 1, 3, …
$ maximum_minimum_nights                       <dbl> 2, 3, 3, 5, 7, 2, 3, 6, 5, 30, 1, 3, …
$ minimum_maximum_nights                       <dbl> 15, 30, 1125, 14, 1125, 10, 1125, 112…
$ maximum_maximum_nights                       <dbl> 15, 30, 1125, 14, 1125, 10, 1125, 112…
$ minimum_nights_avg_ntm                       <dbl> 2.0, 3.0, 3.0, 4.1, 7.0, 2.0, 3.0, 6.…
$ maximum_nights_avg_ntm                       <dbl> 15, 30, 1125, 14, 1125, 10, 1125, 112…
$ calendar_updated                             <chr> "5 months ago", "4 months ago", "7 mo…
$ has_availability                             <lgl> TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, T…
$ availability_30                              <dbl> 29, 28, 29, 21, 0, 0, 8, 0, 11, 0, 0,…
$ availability_60                              <dbl> 59, 58, 59, 21, 0, 0, 8, 0, 24, 0, 0,…
$ availability_90                              <dbl> 89, 88, 89, 21, 0, 0, 8, 5, 24, 26, 0…
$ availability_365                             <dbl> 89, 363, 172, 21, 0, 58, 8, 189, 24, …
$ calendar_last_scraped                        <date> 2020-06-28, 2020-06-28, 2020-06-28, …
$ number_of_reviews                            <dbl> 168, 50, 293, 22, 90, 17, 73, 7, 40, …
$ number_of_reviews_ltm                        <dbl> 1, 4, 31, 2, 0, 0, 1, 0, 0, 1, 11, 1,…
$ first_review                                 <date> 2009-09-04, 2013-12-02, 2010-10-14, …
$ last_review                                  <date> 2019-07-19, 2019-12-14, 2020-03-02, …
$ review_scores_rating                         <dbl> 96, 98, 91, 98, 94, 97, 98, 91, 97, 8…
$ review_scores_accuracy                       <dbl> 10, 10, 10, 10, 10, 10, 10, 10, 10, 9…
$ review_scores_cleanliness                    <dbl> 9, 10, 9, 10, 9, 10, 10, 9, 9, 8, 8, …
$ review_scores_checkin                        <dbl> 10, 10, 10, 10, 10, 10, 10, 10, 10, 1…
$ review_scores_communication                  <dbl> 10, 10, 10, 10, 9, 10, 10, 10, 10, 9,…
$ review_scores_location                       <dbl> 9, 10, 10, 10, 10, 10, 10, 9, 9, 10, …
$ review_scores_value                          <dbl> 9, 10, 9, 10, 9, 9, 9, 9, 10, 9, 9, 9…
$ requires_license                             <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FA…
$ license                                      <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ jurisdiction_names                           <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ instant_bookable                             <lgl> FALSE, FALSE, FALSE, FALSE, TRUE, FAL…
$ is_business_travel_ready                     <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FA…
$ cancellation_policy                          <chr> "moderate", "moderate", "moderate", "…
$ require_guest_profile_picture                <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FA…
$ require_guest_phone_verification             <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FA…
$ calculated_host_listings_count               <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1…
$ calculated_host_listings_count_entire_homes  <dbl> 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 0, 1, 1…
$ calculated_host_listings_count_private_rooms <dbl> 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0…
$ calculated_host_listings_count_shared_rooms  <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
$ reviews_per_month                            <dbl> 1.28, 0.62, 2.48, 0.18, 0.75, 0.14, 0…
#calendar <- read_csv('http://data.insideairbnb.com/denmark/hovedstaden/copenhagen/2020-06-26/data/calendar.csv.gz')
#calendar %>% glimpse()
#reviews <- read_csv('http://data.insideairbnb.com/denmark/hovedstaden/copenhagen/2020-06-26/data/reviews.csv.gz')
#reviews %>% glimpse()
# # And the summary plus geodata
# summaries_listings <- read_csv('http://data.insideairbnb.com/denmark/hovedstaden/copenhagen/2020-06-26/visualisations/listings.csv')
# summaries_reviews <- read_csv('http://data.insideairbnb.com/denmark/hovedstaden/copenhagen/2020-06-26/visualisations/reviews.csv')
# summaries_neighbourhoods <- read_csv('http://data.insideairbnb.com/denmark/hovedstaden/copenhagen/2020-06-26/visualisations/neighbourhoods.csv')
# The geodat of the hoods comes as a geojson, so we need the right package to load it
library(geojsonio)
neighbourhoods_geojson <- geojson_read( 'http://data.insideairbnb.com/denmark/hovedstaden/copenhagen/2020-06-26/visualisations/neighbourhoods.geojson',  what = "sp")

Problem 1: Professional host

listings %>%
  count(host_id, sort = TRUE)
listings %>%
  filter(host_id == 187610263) %>%
  count(neighbourhood_cleansed, sort = TRUE)
listings %<>%
  mutate(price = price %>% parse_number(),
         price_sqf = price / square_feet) 
listings %<>%
  group_by(host_id) %>%
  mutate(host_professional = n() >= 5) %>%
  ungroup()
listings %>%
  group_by(host_professional) %>%
  summarise(review = review_scores_rating %>% mean(na.rm = TRUE),
            price = price %>% mean(na.rm = TRUE))
listings %>%
  group_by(neighbourhood_cleansed, host_professional) %>%
  summarise(review = review_scores_rating %>% mean(na.rm = TRUE)) %>%
  pivot_wider(names_from = host_professional, values_from = review)

Description & Satisfaction

listings %<>%
  mutate(desc_lenght = description %>% str_count('\\w+')) %>%
  mutate(desc_long =  percent_rank(desc_lenght) > 0.9 )
listings %>%
  group_by(desc_long) %>%
  summarise(review = review_scores_rating %>% mean(na.rm =TRUE))

Inspecting & Tidying data

Basic formating

listings %>% skim()
listings %<>%
    mutate(across(is_character, ~ifelse(.x == "", NA, .x)))

Misssing data

library(VIM)
listings %>% 
  select(host_is_superhost, review_scores_rating, host_response_time, name, host_since,zipcode) %>%
  aggr(numbers = TRUE, prop = c(TRUE, FALSE))

Best party place

listings %<>% 
  mutate(party_place = accommodates >= 10) 
listings %>% 
  filter(party_place == TRUE) %>%
  group_by(neighbourhood_cleansed) %>%
  summarize(n = n(),
         review = review_scores_rating %>% mean(na.rm = TRUE),
         price = price %>% mean(na.rm = TRUE) ) %>%
  arrange(desc(n))

EDA

DataViz

Geoplotting

library(leaflet)
listings %>% leaflet() %>%
  addTiles() %>%
  addMarkers(~longitude, ~latitude,
             labelOptions = labelOptions(noHide = F),
             clusterOptions = markerClusterOptions(),
             popup = paste0("<b> Name: </b>", listings$name, 
                            "<br/><b> Host Name: </b>", listings$host_name, 
                            "<br> <b> Price: </b>", listings$price, 
                            "<br/><b> Room Type: </b>", listings$room_type, 
                            "<br/><b> Property Type: </b>", listings$property_type
                 )) %>% 
#  setView(-74.00, 40.71, zoom = 12) %>%
  addProviderTiles("CartoDB.Positron")
# I need to fortify the data AND keep trace of the commune code! (Takes ~2 minutes)
library(broom)
neighbourhoods_tidy <-  neighbourhoods_geojson %>%
  tidy(region = "neighbourhood")
neighbourhoods_tidy %>% glimpse()
neighbourhoods_tidy %>%
  ggplot(aes(x = long, y = lat, group = group)) +
  geom_polygon() +
  theme_void() +
  coord_map()
neighborhood_agg <- listings %>%
  group_by(neighbourhood_cleansed) %>%
  summarise(n = n(),
            price_mean = price %>% mean(na.rm = TRUE),
            review_mean = review_scores_rating %>% mean(na.rm = TRUE))
  
neighbourhoods_tidy %<>%
  left_join(neighborhood_agg, by = c('id' = 'neighbourhood_cleansed'))
neighbourhoods_tidy %>%
  ggplot(aes(x = long, y = lat, group = group, fill = n)) +
  geom_polygon() +
  theme_void() +
  coord_map()
LS0tCnRpdGxlOiAiV29ya3Nob3A6IEV4cGxvcmluZyB0aGUgSW5zaWRlQWlyQm5CIGRhdGFzZXQiCmF1dGhvcjogIkRhbmllbCBTLiBIYWluIChkc2hAYnVzaW5lc3MuYWF1LmRrKSIKZGF0ZTogIlVwZGF0ZWQgYHIgZm9ybWF0KFN5cy50aW1lKCksICclQiAlZCwgJVknKWAiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBkZl9wcmludDogcGFnZWQKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAyCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogZmFsc2UKICAgIHRoZW1lOiBmbGF0bHkKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KIyBLbml0ciBvcHRpb25zCiMjIyBHZW5lcmljIHByZWFtYmxlClN5cy5zZXRlbnYoTEFORyA9ICJlbiIpICMgRm9yIGVuZ2xpc2ggbGFuZ3VhZ2UKb3B0aW9ucyhzY2lwZW4gPSA1KSAjIFRvIGRlYWN0aXZhdGUgYW5ub3lpbmcgc2NpZW50aWZpYyBudW1iZXIgbm90YXRpb24KCiMgcm0obGlzdD1scygpKTsgZ3JhcGhpY3Mub2ZmKCkgIyBnZXQgcmlkIG9mIGV2ZXJ5dGhpbmcgaW4gdGhlIHdvcmtzcGFjZQppZiAoIXJlcXVpcmUoImtuaXRyIikpIGluc3RhbGwucGFja2FnZXMoImtuaXRyIik7IGxpYnJhcnkoa25pdHIpICMgRm9yIGRpc3BsYXkgb2YgdGhlIG1hcmtkb3duCgojIyMgS25pdHIgb3B0aW9ucwprbml0cjo6b3B0c19jaHVuayRzZXQod2FybmluZz1GQUxTRSwKICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZT1GQUxTRSwKICAgICAgICAgICAgICAgICAgICAgZmlnLmFsaWduPSJjZW50ZXIiCiAgICAgICAgICAgICAgICAgICAgICkKYGBgCgojIyBQcmVhbWJsZQoKYGBge3J9CiMgQ2xlYXIgd29ya3NwYWNlCnJtKGxpc3Q9bHMoKSk7IGdyYXBoaWNzLm9mZigpIApgYGAKCmBgYHtyfQojIyMgTG9hZCBwYWNrYWdlcwpsaWJyYXJ5KHRpZHl2ZXJzZSkgIyBDb2xsZWN0aW9uIG9mIGFsbCB0aGUgZ29vZCBzdHVmZiBsaWtlIGRwbHlyLCBnZ3Bsb3QyIGVjdC4KbGlicmFyeShtYWdyaXR0cikgIyBGb3IgZXh0cmEtcGlwaW5nIG9wZXJhdG9ycyAoZWcuICU8PiUpCmxpYnJhcnkoc2tpbXIpICMgRm9yIG5pY2UgZGF0YSBzdW1tYXJpZXMKYGBgCgoKIyBUaGUgSW5zaWRlQWlyQm5CIGRhdGEKCiMjIEluc3Ryb2R1Y3Rpb24KCgoqIFRoZSBkYXRhIGlzIHNvdXJjZWQgZnJvbSB0aGUgWyoqSW5zaWRlIEFpcmJuYioqXShodHRwOi8vaW5zaWRlYWlyYm5iLmNvbS9nZXQtdGhlLWRhdGEuaHRtbCkgd2hpY2ggaG9zdHMgcHVibGljbHkgYXZhaWxhYmxlIGRhdGEgZnJvbSB0aGUgQWlyYm5iIHNpdGUuCiogSW50ZXJhY3RpdmUgdmlzdWFsaXphdGlvbnMgYXJlIHByb3ZpZGVkIFtoZXJlXShodHRwOi8vaW5zaWRlYWlyYm5iLmNvbS9jb3BlbmhhZ2VuLz9uZWlnaGJvdXJob29kPSZmaWx0ZXJFbnRpcmVIb21lcz1mYWxzZSZmaWx0ZXJIaWdobHlBdmFpbGFibGU9ZmFsc2UmZmlsdGVyUmVjZW50UmV2aWV3cz1mYWxzZSZmaWx0ZXJNdWx0aUxpc3RpbmdzPWZhbHNlKQoKVGhlIGRhdGFzZXQgY29tcHJpc2VzIG9mIHRocmVlIG1haW4gdGFibGVzOgoKKiBgbGlzdGluZ3NgIC0gRGV0YWlsZWQgbGlzdGluZ3MgZGF0YSBzaG93aW5nIDk2IGF0dHRyaWJ1dGVzIGZvciBlYWNoIG9mIHRoZSBsaXN0aW5ncy4gU29tZSBvZiB0aGUgYXR0cmlidXRlcyB3aGljaCBhcmUgaW50dWl0aXZseSBpbnRlcmVzdGluZyBhcmU6IGBwcmljZWAgKGNvbnRpbnVvdXMpLCBgbG9uZ2l0dWRlYCAoY29udGludW91cyksIGBsYXRpdHVkZWAgKGNvbnRpbnVvdXMpLCBgbGlzdGluZ190eXBlYCAoY2F0ZWdvcmljYWwpLCBgaXNfc3VwZXJob3N0YCAoY2F0ZWdvcmljYWwpLCBgbmVpZ2hib3VyaG9vZGAgKGNhdGVnb3JpY2FsKSwgYHJhdGluZ3NgIChjb250aW51b3VzKSBhbW9uZyBvdGhlcnMuCiogYHJldmlld3NgIC0gRGV0YWlsZWQgcmV2aWV3cyBnaXZlbiBieSB0aGUgZ3Vlc3RzIHdpdGggNiBhdHRyaWJ1dGVzLiBLZXkgYXR0cmlidXRlcyBpbmNsdWRlIGBkYXRlYCAoZGF0ZXRpbWUpLCBgbGlzdGluZ19pZGAgKGRpc2NyZXRlKSwgYHJldmlld2VyX2lkYCAoZGlzY3JldGUpIGFuZCBgY29tbWVudGAgKHRleHR1YWwpLgoqIGBjYWxlbmRhcmAgLSBQcm92aWRlcyBkZXRhaWxzIGFib3V0IGJvb2tpbmcgZm9yIHRoZSBuZXh0IHllYXIgYnkgbGlzdGluZy4gRm91ciBhdHRyaWJ1dGVzIGluIHRvdGFsIGluY2x1ZGluZyBgbGlzdGluZ19pZGAgKGRpc2NyZXRlKSwgYGRhdGVgIChkYXRldGltZSksIGBhdmFpbGFibGVgIChjYXRlZ29yaWNhbCkgYW5kIGBwcmljZWAgKGNvbnRpbnVvdXMpLgoKIyMgTG9hZCBkYXRhCgpgYGB7cn0KbGlzdGluZ3MgPC0gcmVhZF9jc3YoJ2h0dHA6Ly9kYXRhLmluc2lkZWFpcmJuYi5jb20vZGVubWFyay9ob3ZlZHN0YWRlbi9jb3BlbmhhZ2VuLzIwMjAtMDYtMjYvZGF0YS9saXN0aW5ncy5jc3YuZ3onKQpsaXN0aW5ncyAlPiUgZ2xpbXBzZSgpCmBgYAoKYGBge3J9CiNjYWxlbmRhciA8LSByZWFkX2NzdignaHR0cDovL2RhdGEuaW5zaWRlYWlyYm5iLmNvbS9kZW5tYXJrL2hvdmVkc3RhZGVuL2NvcGVuaGFnZW4vMjAyMC0wNi0yNi9kYXRhL2NhbGVuZGFyLmNzdi5neicpCiNjYWxlbmRhciAlPiUgZ2xpbXBzZSgpCmBgYAoKYGBge3J9CiNyZXZpZXdzIDwtIHJlYWRfY3N2KCdodHRwOi8vZGF0YS5pbnNpZGVhaXJibmIuY29tL2Rlbm1hcmsvaG92ZWRzdGFkZW4vY29wZW5oYWdlbi8yMDIwLTA2LTI2L2RhdGEvcmV2aWV3cy5jc3YuZ3onKQojcmV2aWV3cyAlPiUgZ2xpbXBzZSgpCmBgYAoKYGBge3J9CiMgIyBBbmQgdGhlIHN1bW1hcnkgcGx1cyBnZW9kYXRhCiMgc3VtbWFyaWVzX2xpc3RpbmdzIDwtIHJlYWRfY3N2KCdodHRwOi8vZGF0YS5pbnNpZGVhaXJibmIuY29tL2Rlbm1hcmsvaG92ZWRzdGFkZW4vY29wZW5oYWdlbi8yMDIwLTA2LTI2L3Zpc3VhbGlzYXRpb25zL2xpc3RpbmdzLmNzdicpCiMgc3VtbWFyaWVzX3Jldmlld3MgPC0gcmVhZF9jc3YoJ2h0dHA6Ly9kYXRhLmluc2lkZWFpcmJuYi5jb20vZGVubWFyay9ob3ZlZHN0YWRlbi9jb3BlbmhhZ2VuLzIwMjAtMDYtMjYvdmlzdWFsaXNhdGlvbnMvcmV2aWV3cy5jc3YnKQojIHN1bW1hcmllc19uZWlnaGJvdXJob29kcyA8LSByZWFkX2NzdignaHR0cDovL2RhdGEuaW5zaWRlYWlyYm5iLmNvbS9kZW5tYXJrL2hvdmVkc3RhZGVuL2NvcGVuaGFnZW4vMjAyMC0wNi0yNi92aXN1YWxpc2F0aW9ucy9uZWlnaGJvdXJob29kcy5jc3YnKQpgYGAKYGBge3J9CiMgVGhlIGdlb2RhdCBvZiB0aGUgaG9vZHMgY29tZXMgYXMgYSBnZW9qc29uLCBzbyB3ZSBuZWVkIHRoZSByaWdodCBwYWNrYWdlIHRvIGxvYWQgaXQKbGlicmFyeShnZW9qc29uaW8pCm5laWdoYm91cmhvb2RzX2dlb2pzb24gPC0gZ2VvanNvbl9yZWFkKCAnaHR0cDovL2RhdGEuaW5zaWRlYWlyYm5iLmNvbS9kZW5tYXJrL2hvdmVkc3RhZGVuL2NvcGVuaGFnZW4vMjAyMC0wNi0yNi92aXN1YWxpc2F0aW9ucy9uZWlnaGJvdXJob29kcy5nZW9qc29uJywgIHdoYXQgPSAic3AiKQpgYGAKCiMgUHJvYmxlbSAxOiBQcm9mZXNzaW9uYWwgaG9zdAoKYGBge3J9Cmxpc3RpbmdzICU+JQogIGNvdW50KGhvc3RfaWQsIHNvcnQgPSBUUlVFKQpgYGAKCmBgYHtyfQpsaXN0aW5ncyAlPiUKICBmaWx0ZXIoaG9zdF9pZCA9PSAxODc2MTAyNjMpICU+JQogIGNvdW50KG5laWdoYm91cmhvb2RfY2xlYW5zZWQsIHNvcnQgPSBUUlVFKQpgYGAKCmBgYHtyfQpsaXN0aW5ncyAlPD4lCiAgbXV0YXRlKHByaWNlID0gcHJpY2UgJT4lIHBhcnNlX251bWJlcigpLAogICAgICAgICBwcmljZV9zcWYgPSBwcmljZSAvIHNxdWFyZV9mZWV0KSAKYGBgCgpgYGB7cn0KbGlzdGluZ3MgJTw+JQogIGdyb3VwX2J5KGhvc3RfaWQpICU+JQogIG11dGF0ZShob3N0X3Byb2Zlc3Npb25hbCA9IG4oKSA+PSA1KSAlPiUKICB1bmdyb3VwKCkKYGBgCgpgYGB7cn0KbGlzdGluZ3MgJT4lCiAgZ3JvdXBfYnkoaG9zdF9wcm9mZXNzaW9uYWwpICU+JQogIHN1bW1hcmlzZShyZXZpZXcgPSByZXZpZXdfc2NvcmVzX3JhdGluZyAlPiUgbWVhbihuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBwcmljZSA9IHByaWNlICU+JSBtZWFuKG5hLnJtID0gVFJVRSkpCmBgYAoKYGBge3J9Cmxpc3RpbmdzICU+JQogIGdyb3VwX2J5KG5laWdoYm91cmhvb2RfY2xlYW5zZWQsIGhvc3RfcHJvZmVzc2lvbmFsKSAlPiUKICBzdW1tYXJpc2UocmV2aWV3ID0gcmV2aWV3X3Njb3Jlc19yYXRpbmcgJT4lIG1lYW4obmEucm0gPSBUUlVFKSkgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IGhvc3RfcHJvZmVzc2lvbmFsLCB2YWx1ZXNfZnJvbSA9IHJldmlldykKYGBgCgoKIyBEZXNjcmlwdGlvbiAmIFNhdGlzZmFjdGlvbgoKYGBge3J9Cmxpc3RpbmdzICU8PiUKICBtdXRhdGUoZGVzY19sZW5naHQgPSBkZXNjcmlwdGlvbiAlPiUgc3RyX2NvdW50KCdcXHcrJykpICU+JQogIG11dGF0ZShkZXNjX2xvbmcgPSAgcGVyY2VudF9yYW5rKGRlc2NfbGVuZ2h0KSA+IDAuOSApCmBgYAoKYGBge3J9Cmxpc3RpbmdzICU+JQogIGdyb3VwX2J5KGRlc2NfbG9uZykgJT4lCiAgc3VtbWFyaXNlKHJldmlldyA9IHJldmlld19zY29yZXNfcmF0aW5nICU+JSBtZWFuKG5hLnJtID1UUlVFKSkKYGBgCgoKCgoKCgoKCiMgSW5zcGVjdGluZyAmIFRpZHlpbmcgZGF0YQoKIyMgQmFzaWMgZm9ybWF0aW5nCgpgYGB7cn0KbGlzdGluZ3MgJT4lIHNraW0oKQpgYGAKCmBgYHtyfQpsaXN0aW5ncyAlPD4lCiAgICBtdXRhdGUoYWNyb3NzKGlzX2NoYXJhY3RlciwgfmlmZWxzZSgueCA9PSAiIiwgTkEsIC54KSkpCmBgYAoKCiMjIE1pc3NzaW5nIGRhdGEKCmBgYHtyfQpsaWJyYXJ5KFZJTSkKYGBgCgpgYGB7cn0KbGlzdGluZ3MgJT4lIAogIHNlbGVjdChob3N0X2lzX3N1cGVyaG9zdCwgcmV2aWV3X3Njb3Jlc19yYXRpbmcsIGhvc3RfcmVzcG9uc2VfdGltZSwgbmFtZSwgaG9zdF9zaW5jZSx6aXBjb2RlKSAlPiUKICBhZ2dyKG51bWJlcnMgPSBUUlVFLCBwcm9wID0gYyhUUlVFLCBGQUxTRSkpCmBgYAoKIyBCZXN0IHBhcnR5IHBsYWNlCgpgYGB7cn0KbGlzdGluZ3MgJTw+JSAKICBtdXRhdGUocGFydHlfcGxhY2UgPSBhY2NvbW1vZGF0ZXMgPj0gMTApIApgYGAKCmBgYHtyfQpsaXN0aW5ncyAlPiUgCiAgZmlsdGVyKHBhcnR5X3BsYWNlID09IFRSVUUpICU+JQogIGdyb3VwX2J5KG5laWdoYm91cmhvb2RfY2xlYW5zZWQpICU+JQogIHN1bW1hcml6ZShuID0gbigpLAogICAgICAgICByZXZpZXcgPSByZXZpZXdfc2NvcmVzX3JhdGluZyAlPiUgbWVhbihuYS5ybSA9IFRSVUUpLAogICAgICAgICBwcmljZSA9IHByaWNlICU+JSBtZWFuKG5hLnJtID0gVFJVRSkgKSAlPiUKICBhcnJhbmdlKGRlc2MobikpCmBgYAoKCiMgRURBCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKIyBEYXRhVml6CgojIyBHZW9wbG90dGluZwoKYGBge3J9CmxpYnJhcnkobGVhZmxldCkKYGBgCgoKYGBge3J9Cmxpc3RpbmdzICU+JSBsZWFmbGV0KCkgJT4lCiAgYWRkVGlsZXMoKSAlPiUKICBhZGRNYXJrZXJzKH5sb25naXR1ZGUsIH5sYXRpdHVkZSwKICAgICAgICAgICAgIGxhYmVsT3B0aW9ucyA9IGxhYmVsT3B0aW9ucyhub0hpZGUgPSBGKSwKICAgICAgICAgICAgIGNsdXN0ZXJPcHRpb25zID0gbWFya2VyQ2x1c3Rlck9wdGlvbnMoKSwKICAgICAgICAgICAgIHBvcHVwID0gcGFzdGUwKCI8Yj4gTmFtZTogPC9iPiIsIGxpc3RpbmdzJG5hbWUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxici8+PGI+IEhvc3QgTmFtZTogPC9iPiIsIGxpc3RpbmdzJGhvc3RfbmFtZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPGJyPiA8Yj4gUHJpY2U6IDwvYj4iLCBsaXN0aW5ncyRwcmljZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPGJyLz48Yj4gUm9vbSBUeXBlOiA8L2I+IiwgbGlzdGluZ3Mkcm9vbV90eXBlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnIvPjxiPiBQcm9wZXJ0eSBUeXBlOiA8L2I+IiwgbGlzdGluZ3MkcHJvcGVydHlfdHlwZQogICAgICAgICAgICAgICAgICkpICU+JSAKIyAgc2V0VmlldygtNzQuMDAsIDQwLjcxLCB6b29tID0gMTIpICU+JQogIGFkZFByb3ZpZGVyVGlsZXMoIkNhcnRvREIuUG9zaXRyb24iKQpgYGAKCmBgYHtyfQojIEkgbmVlZCB0byBmb3J0aWZ5IHRoZSBkYXRhIEFORCBrZWVwIHRyYWNlIG9mIHRoZSBjb21tdW5lIGNvZGUhIChUYWtlcyB+MiBtaW51dGVzKQpsaWJyYXJ5KGJyb29tKQpuZWlnaGJvdXJob29kc190aWR5IDwtICBuZWlnaGJvdXJob29kc19nZW9qc29uICU+JQogIHRpZHkocmVnaW9uID0gIm5laWdoYm91cmhvb2QiKQpgYGAKCmBgYHtyfQpuZWlnaGJvdXJob29kc190aWR5ICU+JSBnbGltcHNlKCkKYGBgCgpgYGB7cn0KbmVpZ2hib3VyaG9vZHNfdGlkeSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSkgKwogIGdlb21fcG9seWdvbigpICsKICB0aGVtZV92b2lkKCkgKwogIGNvb3JkX21hcCgpCmBgYApgYGB7cn0KbmVpZ2hib3Job29kX2FnZyA8LSBsaXN0aW5ncyAlPiUKICBncm91cF9ieShuZWlnaGJvdXJob29kX2NsZWFuc2VkKSAlPiUKICBzdW1tYXJpc2UobiA9IG4oKSwKICAgICAgICAgICAgcHJpY2VfbWVhbiA9IHByaWNlICU+JSBtZWFuKG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIHJldmlld19tZWFuID0gcmV2aWV3X3Njb3Jlc19yYXRpbmcgJT4lIG1lYW4obmEucm0gPSBUUlVFKSkKICAKYGBgCgoKYGBge3J9Cm5laWdoYm91cmhvb2RzX3RpZHkgJTw+JQogIGxlZnRfam9pbihuZWlnaGJvcmhvb2RfYWdnLCBieSA9IGMoJ2lkJyA9ICduZWlnaGJvdXJob29kX2NsZWFuc2VkJykpCmBgYAoKYGBge3J9Cm5laWdoYm91cmhvb2RzX3RpZHkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCwgZmlsbCA9IG4pKSArCiAgZ2VvbV9wb2x5Z29uKCkgKwogIHRoZW1lX3ZvaWQoKSArCiAgY29vcmRfbWFwKCkKYGBgCgoKCg==